using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using FoundationLib.Ifc.Domain.Rules;
using System.Linq.Expressions;
using System.Reflection;


namespace FoundationLib.Ifc.Domain.Rules 
{
	/// <summary>
	/// A generic (typed) rule base class
	/// </summary>
	public abstract class GenericRuleBase<T> : RuleBase 
	{
		/// <summary>
		/// The domain object this rule applies to
		/// </summary>
		private T container;

		/// <summary>
		/// Adds the name of an affected member (attribute or property)
		/// </summary>
		/// <param name="member">The name of the affected member</param>
		/// <returns>This rule base instance</returns>
		protected GenericRuleBase<T> AddAffectedMember(Expression<Func<T, object>> member)
		{
			if (member == null)
				throw new ArgumentNullException("member");
			return (GenericRuleBase<T>)this.AddAffectedMember(this.GetMemberName(member));
		}

		/// <summary>
		/// Gets the domain object this rule applies to
		/// </summary>
		protected T Container
		{
			get
			{
				return this.container;
			}
		}

		/// <summary>
		/// Returns the name of the property associated with the given member expression
		/// </summary>
		/// <param name="expression">A member expression referring to a property</param>
		/// <returns>The property name</returns>
		private string GetMemberName(Expression<Func<T, object>> expression)
		{
			MemberExpression memberEx = expression as MemberExpression;
			if (memberEx == null)
				throw new ArgumentException("Member does not refer to a property.", "expression");
			PropertyInfo property = memberEx.Member as PropertyInfo;
			if (property == null)
				throw new ArgumentException("Member does not refer to a property.", "expression");

			return property.Name;
		}

		/// <summary>
		/// Removes the affected member with the given name
		/// </summary>
		/// <returns>True if the member was removed, false otherwise</returns>
		/// <param name="member">The name of the affected member</param>
		protected bool RemoveAffectedMember(Expression<Func<T, object>> member)
		{
			if (member == null)
				throw new ArgumentNullException("member");
			return this.RemoveAffectedMember(this.GetMemberName(member));
		}

		/// <summary>
		/// Creates a new domain rule
		/// </summary>
		/// <param name="id">The domain rule identifier</param>
		/// <param name="container">The domain object instance this rule applies to</param>
		protected GenericRuleBase(string id, T container) : base(id)
		{
			if (container == null)
				throw new ArgumentNullException("container");

			this.container = container;
		}

	}

}